#! /usr/bin/env python
# -*- coding: utf-8 -*-

import gtk
import gobject
from gtk import gdk
import pygtk
from math import sqrt, pi, sin, cos, radians, atan, atan2

from plane import Plane

class StereoGTK(gtk.DrawingArea):
    def __init__(self, pixbuf):
        self.colors = [[1,0,0], [0,0,1], [0,1,0], [1,1,0], [1,0,1], [0,1,1]]
        gtk.DrawingArea.__init__(self)
        self.pixbuf = pixbuf        
        self.width, self.height = pixbuf.get_width(), pixbuf.get_height()
        self.set_size_request (self.width, self.height)
        self.radius = min(self.width / 2, self.height / 2)-5
        self.centr_x, self.centr_y = self.width / 2, self.height / 2 
        self.connect("expose_event", self.expose)

        self.pairs = [] # list of pairs of conj
        self.cur_pair = [] # curently selected pair
        self.cur_max = [] # currently selected max
        self.scats = None # scats directions ans their length to plot
        self.sctress = None # stress axes of cur pair
        self.belts = []
        self.curbelt = None 
    
    def expose(self, widget, event):        
        context = widget.window.cairo_create()
        # set a clip region for the expose event
        context.rectangle(event.area.x, event.area.y,
                          event.area.width, event.area.height)
        context.clip()
        context.set_source_pixbuf(self.pixbuf,0,0)
        context.paint()
        context.stroke()  

        self.Draw(context)
        
    def Draw(self, context):
        # draw belts:
        for b in self.belts:
            self.draw_Belt(context, b)

        # draw curent belt:
        if self.cur_belt:
            self.draw_Belt(context, self.cur_belt, col=[1,0,0])

        # draw scats:
        if self.scats:
            self.draw_Scats(context, self.scats)
        # draw pairs
        for i, pair in enumerate(self.pairs):
            color = self.colors[i% len(self.colors)]
            self.draw_Arrow(context, pair[0], pair[1], color)
            self.draw_Point(context, pair[0], color)
            self.draw_Arrow(context, pair[2], pair[3], color)
            self.draw_Point(context, pair[2], color)
        # draw cur_pair
        if self.cur_pair:
            self.draw_GreatCircle(context, self.cur_pair[0], self.cur_pair[2])
            self.draw_Arrow(context, self.cur_pair[0], self.cur_pair[1],[0,0,0])
            self.draw_Point(context, self.cur_pair[0], [0,0,0])
            self.draw_Arrow(context, self.cur_pair[2], self.cur_pair[3],[0,0,0])
            self.draw_Point(context, self.cur_pair[2], [0,0,0])
            
        # draw cur_max
        if self.cur_max:
            self.draw_Point(context, self.cur_max, [1,0,0])

        # draw stress
        if self.stress:
            self.draw_Stress(context, self.stress)
        
    def draw_Point(self, context, plane, col, linewidth=1, r=3):
        x, y = self.get_coords(plane)
        context.set_source_rgb(col[0], col[1], col[2])
        context.set_line_width (linewidth)
        context.move_to(x + r, y)
        context.arc(x, y, r, 0, 2 * pi)    
        context.fill_preserve()
        context.set_source_rgb(1,1,1)
        context.stroke() 

    def draw_Scats(self, context, scats):
        prof_vals = scats[0]
        prof_dirs = scats[1]
        for i in range (len(prof_dirs)):
            dir = prof_dirs[i]
            val = prof_vals[i]
            self. draw_Arrow(context, self.cur_max, dir, [0,0,0], .5, val, False)

    def draw_Arrow(self, context, plane, direction, col, linewidth=2, \
                   Arrow_length=20, Plot_triangle=True):
        a = (plane.dir - direction) % 360 
        if (a>90 and a<180) or (a<360 and a>270):
            direction = (direction+180)%360
        al = Arrow_length# arrow length
        dplane = plane.get_perpendicular_between(Plane(direction, .0001)).normal()
        angle_val = dplane.return_angle_between(plane)
        angles_sub = dplane.dir-plane.dir
        points = [] # list of points in a profile
        if (angles_sub>0 and angles_sub<180) or\
           (angles_sub<-180 and angles_sub>-360):
            for angle in range(angle_val, angle_val+al, 2):
                a = dplane.rotated(-1*angle)
                if a.dip > 0: a = Plane(a.dir+180, a.dip*-1)
                points.append(a)
        else:
            for angle in range(angle_val, angle_val+al, 2):
                a = dplane.rotated(angle)
                if a.dip > 0: a = Plane(a.dir+180, a.dip*-1)
                points.append(a)
        # draw lines
        for i in range(1, len(points)):
            point = points[i]
            point_back = points[i-1]
            x1,y1  = self.get_coords(point_back)
            x2,y2  = self.get_coords(point)
            if abs(x1-x2)>20 or abs(y1-y2)>20: continue
            context.set_source_rgb(col[0], col[1], col[2])
            context.set_line_width (linewidth)
            context.move_to(x1, y1)
            context.line_to(x2, y2)        
            context.stroke()
        # draw triangle
        if not Plot_triangle: return
        x, y = self.get_coords(points[-2])
        x1, y1 = self.get_coords(points[-1])
        angl= atan2((y1-y),(x1-x)) + (radians(90))
        rv = 20 # length of a triangle lines
        xv1, yv1 = x1-sin(angl+radians(10))*rv, y1+cos(angl+radians(10))*rv
        xv2, yv2 = x1-sin(angl-radians(10))*rv, y1+cos(angl-radians(10))*rv
        #line xv1,yv1 - x1,y1
        context.set_source_rgb(col[0], col[1], col[2])
        context.set_line_width (.5*linewidth)
        context.move_to(xv1, yv1)
        context.line_to(x1, y1)        
        context.stroke()
        #line xv2,yv2 - x1,y1
        context.set_source_rgb(col[0], col[1], col[2])
        context.set_line_width (.5*linewidth)
        context.move_to(xv2, yv2)
        context.line_to(x1, y1)        
        context.stroke()

    def draw_GreatCircle(self,context, plane1, plane2, col=[.5,.5,.5], linewidth=1):
        pplane = plane1.get_perpendicular_between(plane2).normal()
        points = []
        for angle in range (0, 180, 1):
            a = pplane.rotated(angle)
            points.append(a)
        # draw lines
        for i in range(1, len(points)):
            point = points[i]
            point_back = points[i-1]
            x1,y1  = self.get_coords(point_back)
            x2,y2  = self.get_coords(point)
            if abs(x1-x2)>20 or abs(y1-y2)>20: continue
            context.set_source_rgb(col[0], col[1], col[2])
            context.set_line_width (linewidth)
            context.move_to(x1, y1)
            context.line_to(x2, y2)        
            context.stroke()

    def draw_Belt(self,context, pplane, col=[0,0,0], linewidth=1):
        points = []
        for angle in range (0, 180, 1):
            a = pplane.rotated(angle)
            points.append(a)
        # draw lines
        for i in range(1, len(points)):
            point = points[i]
            point_back = points[i-1]
            x1,y1  = self.get_coords(point_back)
            x2,y2  = self.get_coords(point)
            if abs(x1-x2)>20 or abs(y1-y2)>20: continue
            context.set_source_rgb(col[0], col[1], col[2])
            context.set_line_width (linewidth)
            context.move_to(x1, y1)
            context.line_to(x2, y2)        
            context.stroke()

    def draw_Stress(self, ctx, stress):
        # draw axes
        self.draw_Point(ctx, Plane(stress[0][0], stress[0][1]), [1,0,0])
        self.draw_Point(ctx, Plane(stress[2][0], stress[2][1]), [1,0,0])

        # draw stress arrows
        cx, cy = self.centr_x, self.centr_y
        radius = self.radius 
        s1 = stress[0]; s3 = stress[2]
        st3_1_x = cx + sin(radians(s3[0]))*radius*1.1
        st3_1_y = cy - cos(radians(s3[0]))*radius*1.1
        st3_o_y = cy - cos(radians(s3[0]))*radius*1.2
        st3_o_x = cx + sin(radians(s3[0]))*radius*1.2
        st3_2_x = st3_o_x + sin(radians(s3[0]+90))*radius*.05
        st3_2_y = st3_o_y - cos(radians(s3[0]+90))*radius*.05
        st3_3_x = st3_o_x + sin(radians(s3[0]-90))*radius*.05
        st3_3_y = st3_o_y - cos(radians(s3[0]-90))*radius*.05

        ctx.set_source_rgb(0,0,0)
        ctx.set_line_width (1)
        ctx.move_to(st3_1_x, st3_1_y)
        ctx.line_to(st3_2_x, st3_2_y)
        ctx.line_to(st3_3_x, st3_3_y)
        ctx.line_to(st3_1_x, st3_1_y)
        ctx.fill_preserve()
        ctx.set_source_rgb(1,1,1)
        ctx.stroke()


        s3[0]+=180
        st3_1_x = cx + sin(radians(s3[0]))*radius*1.1
        st3_1_y = cy - cos(radians(s3[0]))*radius*1.1
        st3_o_y = cy - cos(radians(s3[0]))*radius*1.2
        st3_o_x = cx + sin(radians(s3[0]))*radius*1.2
        st3_2_x = st3_o_x + sin(radians(s3[0]+90))*radius*.05
        st3_2_y = st3_o_y - cos(radians(s3[0]+90))*radius*.05
        st3_3_x = st3_o_x + sin(radians(s3[0]-90))*radius*.05
        st3_3_y = st3_o_y - cos(radians(s3[0]-90))*radius*.05

        ctx.set_source_rgb(0,0,0)
        ctx.set_line_width (1)
        ctx.move_to(st3_1_x, st3_1_y)
        ctx.line_to(st3_2_x, st3_2_y)
        ctx.line_to(st3_3_x, st3_3_y)
        ctx.line_to(st3_1_x, st3_1_y)
        ctx.fill_preserve()
        ctx.set_source_rgb(1,1,1)
        ctx.stroke()
        
        st1_1_x = cx + sin(radians(s1[0]))*radius*1.2
        st1_1_y = cy - cos(radians(s1[0]))*radius*1.2
        st1_o_y = cy - cos(radians(s1[0]))*radius*1.1
        st1_o_x = cx + sin(radians(s1[0]))*radius*1.1
        st1_2_x = st1_o_x + sin(radians(s1[0]+90))*radius*.05
        st1_2_y = st1_o_y - cos(radians(s1[0]+90))*radius*.05
        st1_3_x = st1_o_x + sin(radians(s1[0]-90))*radius*.05
        st1_3_y = st1_o_y - cos(radians(s1[0]-90))*radius*.05

        ctx.set_source_rgb(1,1,1)
        ctx.set_line_width (1)
        ctx.move_to(st1_1_x, st1_1_y)
        ctx.line_to(st1_2_x, st1_2_y)
        ctx.line_to(st1_3_x, st1_3_y)
        ctx.line_to(st1_1_x, st1_1_y)
        ctx.fill_preserve()
        ctx.set_source_rgb(0,0,0)
        ctx.stroke()

        s1[0]+=180
        st1_1_x = cx + sin(radians(s1[0]))*radius*1.2
        st1_1_y = cy - cos(radians(s1[0]))*radius*1.2
        st1_o_y = cy - cos(radians(s1[0]))*radius*1.1
        st1_o_x = cx + sin(radians(s1[0]))*radius*1.1
        st1_2_x = st1_o_x + sin(radians(s1[0]+90))*radius*.05
        st1_2_y = st1_o_y - cos(radians(s1[0]+90))*radius*.05
        st1_3_x = st1_o_x + sin(radians(s1[0]-90))*radius*.05
        st1_3_y = st1_o_y - cos(radians(s1[0]-90))*radius*.05

        ctx.set_source_rgb(1,1,1)
        ctx.set_line_width (1)
        ctx.move_to(st1_1_x, st1_1_y)
        ctx.line_to(st1_2_x, st1_2_y)
        ctx.line_to(st1_3_x, st1_3_y)
        ctx.line_to(st1_1_x, st1_1_y)
        ctx.fill_preserve()
        ctx.set_source_rgb(0,0,0)
        ctx.stroke()

    def get_coords(self, pln, hemisphere = 'lower'):
        if (hemisphere == 'lower' and pln.cos2 < 0) or \
                                (hemisphere == 'upper' and pln.cos2 > 0) :
            return pln.cos3*self.radius + self.centr_x, \
                    (-1) * pln.cos1*self.radius + self.centr_y
        else:
            return (-1) * pln.cos3* self.radius + self.centr_x, \
                    pln.cos1 * self.radius + self.centr_y 
            
            
    def get_widget_coords(self, x, y):
        cos3=(x-self.centr_x)/self.radius
        cos1=(self.centr_y-y)/self.radius 
        if cos1==0: return None,None
        cos1cos3 = cos1**2+cos3**2
        if cos1cos3 < 1: 
            cos2 = sqrt(1-cos1cos3) 
            a = Plane(0,0)
            a.define_by_normal_cos(cos1,cos2,cos3)
            return a.dir, a.dip        
        else:
            return None, None
